package cn.jasonone.jame;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.jasonone.jame.basic.*;
import cn.jasonone.jame.cycle.GameSceneLifeCycle;
import cn.jasonone.jame.model.*;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Duration;
import lombok.*;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 游戏场景父类
 *
 * @author Jason
 * @since 1.0.0
 */
@Getter
@Setter
@ToString(exclude = "gameContext")
@EqualsAndHashCode(exclude = "gameContext")
@Slf4j
public class GameScene extends Scene implements GameSceneLifeCycle, Draw, KeyEventHandler, MouseEventHandler, UniquelyIdentifies {
    /**
     * 场景ID
     */
    private String id;
    @Setter(AccessLevel.NONE)
    @Getter(AccessLevel.NONE)
    private Timeline timeline = new Timeline();
    /**
     * 当前FPS
     */
    @Getter(AccessLevel.NONE)
    private int fps = 60;

    private KeyFrame keyFrame;
    @Getter(AccessLevel.PROTECTED)
    @Setter(AccessLevel.PROTECTED)
    private Group groupPane;
    @Getter
    @Setter(AccessLevel.PROTECTED)
    private Stage stage;
    /**
     * 背景色
     */
    private Color backgroundColor = Color.BLACK;

    private GameContext<GameScene> gameContext;
    /**
     * 精灵容器
     */
    @Setter(AccessLevel.NONE)
    private List<Spirit> spirits = new CopyOnWriteArrayList<>();
    /*
     * 按键事件集合
     */
    private final Map<String, KeyEventHandler> keyEvents = new ConcurrentHashMap<>();
    /**
     * 鼠标监听器
     */
    private final Map<String, MouseEventHandler> mouseEvents = new ConcurrentHashMap<>();

    private final ThreadLocal<MouseEvent> mouseEventThreadLocal = new ThreadLocal<>();

    private final Canvas canvas;
    @Getter
    private GraphicsContext graphicsContext2D;

    public GameScene(double width, double height) {
        this("GameScene_" + IdUtil.fastUUID(), width, height);
    }

    /**
     * 创建一个游戏场景
     *
     * @param id     场景ID
     * @param width  宽度
     * @param height 高度
     */
    public GameScene(String id, double width, double height) {
        super(new Group(), width, height);
        Objects.requireNonNull(id, "游戏场景ID不能为null");
        this.setId(id);
        this.groupPane = (Group) super.getRoot();
        this.canvas = new Canvas(width, height);
        this.graphicsContext2D = canvas.getGraphicsContext2D();
        this.groupPane.getChildren().add(canvas);
    }

    /**
     * 添加键盘事件
     *
     * @param handler 事件处理器
     */
    public void addKeyListener(String id, KeyEventHandler handler) {
        this.keyEvents.put(id, handler);
    }

    /**
     * 添加鼠标事件
     *
     * @param handler 事件处理器
     */
    public void addMouseListener(String id, MouseEventHandler handler) {
        this.mouseEvents.put(id, handler);
    }


    @Override
    public void init(GameContext<JameApplication> gc) {
        gameContext = new DefaultGameContext<>(this, gc);
        timeline.setCycleCount(Timeline.INDEFINITE);
        keyFrame = new KeyFrame(Duration.millis(1000 / fps), new EventHandler<ActionEvent>() {
            private long time;
            //采样率
            private int samplingRate;

            private int fps = GameScene.this.fps;

            private GLabel fpsLabel = new GLabel();

            private GLabel spiritLabel = new GLabel();

            private GLabel keyListenerLabel = new GLabel();
            private GLabel mouseListenerLabel = new GLabel();

            {
                fpsLabel.getVisible().bindBidirectional(DebugHelper.fpsProperty());
                spiritLabel.getVisible().bindBidirectional(DebugHelper.spiritProperty());
                keyListenerLabel.getVisible().bindBidirectional(DebugHelper.keyListenerProperty());
                mouseListenerLabel.getVisible().bindBidirectional(DebugHelper.mouseListenerProperty());
            }

            private void init(Font font, Color color) {
                fpsLabel.setFont(font);
                fpsLabel.setFillColor(Color.WHITE);
                fpsLabel.moveTo(20, font.getSize() + 5);
                fpsLabel.setVisible(DebugHelper.isFps());
                spiritLabel.setFont(font);
                spiritLabel.setFillColor(Color.WHITE);
                spiritLabel.moveTo(20, (font.getSize() + 5) * 2);
                spiritLabel.setVisible(DebugHelper.isSpirit());
                keyListenerLabel.setFont(font);
                keyListenerLabel.setFillColor(Color.WHITE);
                keyListenerLabel.moveTo(20, (font.getSize() + 5) * 3);
                keyListenerLabel.setVisible(DebugHelper.isKeyListener());
                mouseListenerLabel.setFont(font);
                mouseListenerLabel.setFillColor(Color.WHITE);
                mouseListenerLabel.moveTo(20, (font.getSize() + 5) * 4);
                mouseListenerLabel.setVisible(DebugHelper.isMouseListener());
            }

            private void drawDebug(GraphicsContext g) {
                init(g.getFont(), Color.WHITE);
                drawFps(g);
                spiritLabel.setText("精灵数量:{}", spirits.size());
                keyListenerLabel.setText("键盘监听器数量:{}", keyEvents.size());
                mouseListenerLabel.setText("鼠标监听器数量:{}", mouseEvents.size());

                fpsLabel.draw(g);
                spiritLabel.draw(g);
                keyListenerLabel.draw(g);
                mouseListenerLabel.draw(g);
            }

            private void drawFps(GraphicsContext g) {
                if (samplingRate >= GameScene.this.fps / 5) {
                    if (time > 0) {
                        long l = System.nanoTime() - time;
                        l = l / samplingRate;
                        fps = Math.toIntExact(1000_000_000 / l);
                    }
                    samplingRate = 0;
                } else {
                    if (samplingRate == 0) {
                        time = System.nanoTime();
                    }
                    samplingRate++;
                }
                fpsLabel.setText("FPS:{} 采样率:{}", fps, GameScene.this.fps / 5);
                if (DebugHelper.isBorder()) {
                    List<Spirit> spirits = getSpirits();
                    for (Spirit spirit : spirits) {
                        g.setStroke(Color.RED);
                        g.strokeRect(spirit.getX(), spirit.getY(), spirit.getWidth(), spirit.getHeight());
                    }
                }
            }

            @Override
            public void handle(ActionEvent event) {
                //得到画笔
                GraphicsContext graphicsContext = getGraphicsContext2D();
                //设置背景色
                graphicsContext.setFill(backgroundColor);
                //清空屏幕
                graphicsContext.fillRect(0, 0, getWidth(), getHeight());
                //调用update方法
                update();
                //绘制
                GameScene.this.draw(graphicsContext);
                //绘制调试信息
                drawDebug(graphicsContext);
            }
        }, new javafx.animation.KeyValue[]{});
        timeline.getKeyFrames().add(keyFrame);
    }

    public final void remove() {
        gameContext.getRoot();
    }

    @Override
    public void visible(GameContext<JameApplication> gc, boolean visible) {
        if (visible) {
            //如果场景显示,则开始播放时间轴
            this.timeline.play();
        } else {
            //如果场景隐藏,则暂停播放
            this.timeline.pause();
        }
    }

    @Override
    public void destory(GameContext<JameApplication> gc) {
        //如果场景被销毁,则停止时间轴
        this.timeline.stop();
    }

    @Override
    public GameContext<GameScene> getGameContext() {
        return this.gameContext;
    }

    @Override
    public int getFloor() {
        return 0;
    }

    /**
     * 添加控件到场景顶部
     *
     * @param node   控件
     * @param center 是否水平居中
     */
    public void appendTop(Node node, boolean center) {
        this.appendTop(node, getRect(), center);
    }

    /**
     * 添加控件到区域顶部
     *
     * @param node   控件
     * @param rect   相对区域
     * @param center 是否水平居中
     */
    public void appendTop(Node node, Rect rect, boolean center) {
        node.setLayoutY(rect.getY());
        this.append(node, rect, center, false);
    }

    /**
     * 添加控件到场景底部
     *
     * @param node   控件
     * @param center 是否水平居中
     */
    public void appendBottom(Node node, boolean center) {
        this.appendBottom(node, getRect(), center);
    }

    /**
     * 添加控件到区域底部
     *
     * @param node   控件
     * @param rect   相对区域
     * @param center 是否水平居中
     */
    public void appendBottom(Node node, Rect rect, boolean center) {
        if (node.prefHeight(-1) > 0) {
            node.setLayoutY(rect.getHeight() - node.prefHeight(-1));
        } else {
            log.warn("控件[{}]的高度为0,无法设置最底部显示", node.getClass());
        }
        this.append(node, rect, center, false);
    }

    /**
     * 添加控件到场景左部
     *
     * @param node   控件
     * @param center 是否垂直居中
     */
    public void appendLeft(Node node, boolean center) {
        this.appendLeft(node, getRect(), center);
    }

    /**
     * 添加控件到区域左部
     *
     * @param node   控件
     * @param rect   相对区域
     * @param center 是否垂直居中
     */
    public void appendLeft(Node node, Rect rect, boolean center) {
        node.setLayoutX(0);
        this.append(node, rect, false, center);
    }

    /**
     * 添加控件到场景右部
     *
     * @param node   控件
     * @param center 是否垂直居中
     */
    public void appendRight(Node node, boolean center) {
        this.appendRight(node, getRect(), center);
    }

    /**
     * 添加控件到区域左部
     *
     * @param node   控件
     * @param rect   相对区域
     * @param center 是否垂直居中
     */
    public void appendRight(Node node, Rect rect, boolean center) {
        if (node.prefWidth(-1) > 0) {
            node.setLayoutX(rect.getWidth() - node.prefWidth(-1));
        } else {
            log.warn("控件[{}]的宽度为0,无法设置最右显示", node.getClass());
        }
        this.append(node, rect, false, center);
    }

    /**
     * 添加控件到场景中
     *
     * @param node   控件对象
     * @param center 是否水平垂直居中
     */
    public void append(Node node, boolean center) {
        this.append(node, getRect(), center);
    }

    /**
     * 添加控件到场景中
     *
     * @param node   控件
     * @param rect   相对区域
     * @param center 是否相对区域居中
     */
    public void append(Node node, Rect rect, boolean center) {
        this.append(node, rect, center, center);
    }

    /**
     * 添加控件到场景中
     *
     * @param node             控件
     * @param centerHorizontal 是否水平居中
     * @param centerVertical   是否垂直居中
     */
    public void append(Node node, boolean centerHorizontal, boolean centerVertical) {
        this.append(node, getRect(), centerHorizontal, centerVertical);
    }

    /**
     * 添加控件到场景中
     *
     * @param node             控件
     * @param rect             相对区域
     * @param centerHorizontal 是否相对区域水平居中
     * @param centerVertical   是否相对区域垂直居中
     */
    public void append(Node node, Rect rect, boolean centerHorizontal, boolean centerVertical) {
        node.setLayoutX(rect.getX() + node.getLayoutX());
        node.setLayoutY(rect.getY() + node.getLayoutY());
        if (centerHorizontal) {
            node.setLayoutX(rect.getX() + (rect.getWidth() + node.prefWidth(-1)) / 2);
            node.setLayoutY(node.getLayoutY());
        }

        if (centerVertical) {
            node.setLayoutX(node.getLayoutX());
            node.setLayoutY(rect.getY() + (rect.getHeight() + node.prefHeight(-1)) / 2);
        }
        append(node);
    }

    /**
     * 添加控件到场景中
     *
     * @param node 控件
     */
    public void append(Node node) {
        this.groupPane.getChildren().add(node);
    }

    /**
     * 添加精灵到场景中
     *
     * @param spirit 待添加的精灵对象
     */
    public void append(Spirit spirit) {
        Objects.requireNonNull(spirit, "精灵不能为null");
        spirit.setScene(this);
        if (this.spirits.add(spirit)) {
            //生命周期
            spirit.init(getGameContext());
            if (spirit.isContains(getRect())) {
                spirit.setVisible(true);
                spirit.visible(getGameContext(), true);
            }
        }

        //监听事件
        this.addKeyListener(spirit.getId(), spirit);
        this.addMouseListener(spirit.getId(), spirit);
    }

    /**
     * 将精灵添加到场景顶部
     *
     * @param spirit 精灵对象
     * @param center 是否对齐
     */
    public void appendTop(Spirit spirit, boolean center) {
        this.appendTop(spirit, getRect(), center);
    }

    /**
     * 将精灵添加到场景顶部
     *
     * @param spirit 精灵对象
     * @param rect   对齐区域
     * @param center 是否对齐
     */
    public void appendTop(Spirit spirit, Rect rect, boolean center) {
        spirit.moveTo(rect.getX() + spirit.getX(), rect.getY());
        this.append(spirit, rect, center, false);
    }

    /**
     * 将精灵添加到场景下部
     *
     * @param spirit 精灵对象
     * @param center 是否对齐
     */
    public void appendBottom(Spirit spirit, boolean center) {
        this.appendBottom(spirit, getRect(), center);
    }

    /**
     * 将精灵添加到场景下部
     *
     * @param spirit 精灵对象
     * @param rect   对齐区域
     * @param center 是否对齐
     */
    public void appendBottom(Spirit spirit, Rect rect, boolean center) {
        spirit.moveTo(rect.getX() + spirit.getX(), rect.getY() + rect.getHeight() - spirit.getHeight());
        this.append(spirit, rect, center, false);
    }

    /**
     * 将精灵添加到场景左边
     *
     * @param spirit 精灵对象
     * @param center 是否对齐
     */
    public void appendLeft(Spirit spirit, boolean center) {
        this.appendLeft(spirit, getRect(), center);
    }

    /**
     * 将精灵添加到场景左边
     *
     * @param spirit 精灵对象
     * @param rect   对齐区域
     * @param center 是否对齐
     */
    public void appendLeft(Spirit spirit, Rect rect, boolean center) {
        spirit.moveTo(rect.getX(), rect.getY() + spirit.getY());
        this.append(spirit, rect, false, center);
    }

    /**
     * 将精灵添加到场景右边
     *
     * @param spirit 精灵对象
     * @param center 是否对齐
     */
    public void appendRight(Spirit spirit, boolean center) {
        this.appendRight(spirit, getRect(), center);
    }

    /**
     * 将精灵添加到场景右边
     *
     * @param spirit 精灵对象
     * @param rect   对齐区域
     * @param center 是否对齐
     */
    public void appendRight(Spirit spirit, Rect rect, boolean center) {
        spirit.moveTo(rect.getX() + rect.getWidth() - spirit.getWidth(), rect.getY() + spirit.getY());
        this.append(spirit, rect, false, center);
    }

    /**
     * 添加精灵到场景中
     *
     * @param spirit 精灵对象
     * @param center 水平垂直居中
     */
    public void append(Spirit spirit, boolean center) {
        this.append(spirit, getRect(), center, center);
    }

    /**
     * 添加精灵到场景中
     *
     * @param spirit 精灵对象
     * @param rect   对齐区域
     * @param center 水平垂直居中
     */
    public void append(Spirit spirit, Rect rect, boolean center) {
        this.append(spirit, rect, center, center);
    }

    /**
     * 添加精灵到场景中,并相对指定区域对齐
     *
     * @param spirit           精灵对象
     * @param centerHorizontal 水平居中
     * @param centerVertical   垂直居中
     */
    public void append(Spirit spirit, boolean centerHorizontal, boolean centerVertical) {
        this.append(spirit, getRect(), centerHorizontal, centerVertical);
    }

    /**
     * 添加精灵到场景中,并相对指定区域对齐
     *
     * @param spirit           精灵对象
     * @param rect             对齐区域
     * @param centerHorizontal 水平居中
     * @param centerVertical   垂直居中
     */
    public void append(Spirit spirit, Rect rect, boolean centerHorizontal, boolean centerVertical) {
        if (getWidth() == 0 && getHeight() == 0) {
            throw new RuntimeException("场景的宽度或高度不能为0");
        }
        if (centerHorizontal) {
            spirit.moveTo(rect.getX() + (rect.getWidth() - spirit.getWidth()) / 2, spirit.getY());
        }
        if (centerVertical) {
            spirit.moveTo(spirit.getX(), rect.getY() + (rect.getHeight() - spirit.getHeight()) / 2);
        }
        this.append(spirit);
    }

    /**
     * 根据ID查询精灵
     *
     * @param id  精灵ID
     * @param <T> 精灵类型
     * @return 精灵对象
     */
    public <T extends Spirit> T findById(String id) {
        Objects.requireNonNull(id, "精灵ID不能为null");
        for (Spirit spirit : this.spirits) {
            if (id.equals(spirit.getId())) {
                return (T) spirit;
            }
        }
        return null;
    }

    /**
     * 获得指定类型的精灵
     *
     * @param type 精灵的类型
     * @param <T>  精灵类型
     * @return 精灵集合
     */
    public <T extends Spirit> List<T> getSpirits(Class<T> type) {
        List<T> list = new ArrayList<>();
        Iterator<Spirit> iterator = this.spirits.iterator();
        while (iterator.hasNext()) {
            Spirit spirit = iterator.next();
            if (type.isInstance(spirit)) {
                list.add((T) spirit);
            }
        }
        return list;
    }

    /**
     * 删除指定精灵对象
     *
     * @param spirit 精灵对象
     */
    public void remove(Spirit spirit) {
        Objects.requireNonNull(spirit, "待删除的精灵不能为null");
        remove(spirit.getId());
    }

    /**
     * 移除指定ID的事件监听
     *
     * @param id 事件监听ID
     */
    public void removeKeyListener(String id) {
        this.keyEvents.remove(id);
    }

    /**
     * 移除指定ID的事件监听
     *
     * @param id 事件监听ID
     */
    public void removeMouseListener(String id) {
        this.mouseEvents.remove(id);
    }

    /**
     * 根据ID删除精灵
     *
     * @param id 精灵ID
     */
    public void remove(String id) {
        Objects.requireNonNull(id, "精灵ID不能为null");
        //查找精灵
        Spirit spirit = findById(id);
        if (spirit == null) {
            throw new RuntimeException("精灵不存在");
        }
        //删除精灵
        if (this.spirits.remove(spirit)) {
            //销毁精灵
            try {
                spirit.destory(this.getGameContext());
            } finally {
                this.removeKeyListener(spirit.getId());
                this.removeMouseListener(spirit.getId());
            }

        }
    }

    @Override
    public void draw(GraphicsContext gc) {
        if (!spirits.isEmpty()) {
            Paint color = gc.getFill();
            Paint stroke = gc.getStroke();
            Font font = gc.getFont();
            Iterator<Spirit> iterator = this.spirits.iterator();
            while (iterator.hasNext()) {
                Spirit spirit = iterator.next();
                //生命周期
                spirit.update();
                spirit.draw(gc);
                gc.setStroke(stroke);
                gc.setFont(font);
                gc.setFill(color);
            }
        }
    }

    @Override
    public void update() {
        Collections.sort(this.spirits);//排序
    }

    @Override
    public int compareTo(Draw o) {
        return 0;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        Iterator<Map.Entry<String, KeyEventHandler>> iterator = this.keyEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, KeyEventHandler> entry = iterator.next();
            entry.getValue().keyPressed(e);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        Iterator<Map.Entry<String, KeyEventHandler>> iterator = this.keyEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, KeyEventHandler> entry = iterator.next();
            entry.getValue().keyReleased(e);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
        Iterator<Map.Entry<String, KeyEventHandler>> iterator = this.keyEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, KeyEventHandler> entry = iterator.next();
            entry.getValue().keyTyped(e);
        }
    }

    @Override
    public void keyClicked(KeyEvent e) {
        Iterator<Map.Entry<String, KeyEventHandler>> iterator = this.keyEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, KeyEventHandler> entry = iterator.next();
            entry.getValue().keyClicked(e);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        Iterator<Map.Entry<String, MouseEventHandler>> iterator = this.mouseEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, MouseEventHandler> entry = iterator.next();
            MouseEventHandler handler = entry.getValue();
            if (handler.getRect() == null || handler.getRect().isContains(e.getX(), e.getY())) {
                handler.mouseClicked(e);
            }
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Iterator<Map.Entry<String, MouseEventHandler>> iterator = this.mouseEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, MouseEventHandler> entry = iterator.next();
            MouseEventHandler handler = entry.getValue();
            handler.mouseDragged(e);
            MouseEvent event = mouseEventThreadLocal.get();
            if (event != null && handler.getRect() != null) {
                //上次坐标不在区域内
                if (!handler.getRect().isContains(event.getX(), event.getY())) {
                    //本次坐标在区域内
                    if (handler.getRect().isContains(e.getX(), e.getY())) {
                        handler.mouseExited(new MouseEvent(MouseEvent.MOUSE_ENTERED, e.getX(), e.getY(), e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), e.getPickResult()));
                    }
                } else {
                    //本次坐标不在区域内
                    if (!handler.getRect().isContains(e.getX(), e.getY())) {
                        handler.mouseExited(new MouseEvent(MouseEvent.MOUSE_EXITED, e.getX(), e.getY(), e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), e.getPickResult()));
                    }
                }
            }
            mouseEventThreadLocal.set(e);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        MouseEvent event = mouseEventThreadLocal.get();
        Iterator<Map.Entry<String, MouseEventHandler>> iterator = this.mouseEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, MouseEventHandler> entry = iterator.next();
            MouseEventHandler handler = entry.getValue();
            handler.mouseMoved(e);
            if (event != null && handler.getRect() != null) {
                //上次坐标不在区域内
                if (!handler.getRect().isContains(event.getX(), event.getY())) {
                    //本次坐标在区域内
                    if (handler.getRect().isContains(e.getX(), e.getY())) {
                        handler.mouseEntered(new MouseEvent(MouseEvent.MOUSE_ENTERED, e.getX(), e.getY(), e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), e.getPickResult()));
                    }
                } else {
                    //本次坐标不在区域内
                    if (!handler.getRect().isContains(e.getX(), e.getY())) {
                        handler.mouseExited(new MouseEvent(MouseEvent.MOUSE_EXITED, e.getX(), e.getY(), e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), e.getPickResult()));
                    }
                }
            }
        }
        mouseEventThreadLocal.set(e);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        Iterator<Map.Entry<String, MouseEventHandler>> iterator = this.mouseEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, MouseEventHandler> entry = iterator.next();
            MouseEventHandler handler = entry.getValue();
            if (handler.getRect() == null || handler.getRect().isContains(e.getX(), e.getY())) {
                handler.mousePressed(e);
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        Iterator<Map.Entry<String, MouseEventHandler>> iterator = this.mouseEvents.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, MouseEventHandler> entry = iterator.next();
            MouseEventHandler handler = entry.getValue();
            if (handler.getRect() == null || handler.getRect().isContains(e.getX(), e.getY())) {
                handler.mouseReleased(e);
            }
        }
    }

    @Override
    public Rect getRect() {
        return new Rect(0, 0, getWidth(), getHeight());
    }

    @Override
    public void setId(@NonNull String id) {
        if (StrUtil.isNotBlank(id)) {
            this.id = id;
        }
    }

    @Override
    public String getId() {
        if (StrUtil.isBlank(id)) {
            this.id = createId();
        }
        return id;
    }
}
